package com.sinosoft.hydra.um.services;

import cn.com.sinux.spring.utils.*;
import com.sinosoft.hydra.logging.LogModules;
import com.sinosoft.hydra.um.condition.SearchMessageCondition;
import com.sinosoft.hydra.um.condition.SearchReceiveMessageCondition;
import com.sinosoft.hydra.um.constant.BaseConstant;
import com.sinosoft.hydra.um.domain.model.Message;
import com.sinosoft.hydra.um.domain.permission.SysUserMember;
import com.sinosoft.hydra.um.domain.permission.TMessage;
import com.sinosoft.hydra.um.domain.permission.TMessageReceiver;
import cn.com.sinux.spring.exception.ServiceException;
import com.sinosoft.hydra.um.helper.AuthContextHelper;
import com.sinosoft.hydra.um.repositories.SysUserRepository;
import com.sinosoft.hydra.um.repositories.TMessageDAO;
import com.sinosoft.hydra.um.repositories.TMessageReceiverDAO;
import com.sinosoft.hydra.um.services.component.message.MessageSendProxy;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.util.*;

/**
 * Created by zyd on 2015/9/17.
 */
@Service
@Transactional
public class MessageService {


    @Autowired
    private TMessageDAO tMessageDAO;
    @Autowired
    private TMessageReceiverDAO messageReceiverDAO;
    @Autowired
    private List<MessageSendProxy> messageSendProxies;
    @Autowired
    private SysUserRepository sysUserRepository;

    /**
     * 获取所有接收到的消息
     *
     * @param pageable
     * @return
     */
    public Page<TMessageReceiver> findAllMessageReceiver(SearchReceiveMessageCondition searchReceiveMessageCondition, Pageable pageable) {
        final SearchReceiveMessageCondition condition = searchReceiveMessageCondition == null ?
                new SearchReceiveMessageCondition()
                : searchReceiveMessageCondition;
        Specification<TMessageReceiver> messageReceiverSpecification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TMessageReceiver>() {

            public void fillParam(Root<TMessageReceiver> root, CriteriaBuilder cb, List<Predicate> list) {
                if (StringUtils.isNotBlank(condition.getReceiver())) {
                    list.add(cb.equal(root.get("receiver").as(String.class), condition.getReceiver()));
                }
                if (condition.getBeginTime() != null) {
                    list.add(cb.greaterThanOrEqualTo(root.get("tMessage").get("sendedTime").as(Date.class), DateUtils.getStartOfDay(condition.getBeginTime())));
                }
                if (StringUtils.isNotBlank(condition.getReadStatus())) {
                    list.add(cb.equal(root.get("readStatus").as(String.class), condition.getReadStatus()));
                }
                if (StringUtils.isNotBlank(condition.getMessageId())) {
                    list.add(cb.equal(root.get("tMessage").get("id").as(String.class), condition.getMessageId()));
                }
                if (StringUtils.isNotBlank(condition.getReceiverStatus())) {
                    list.add(cb.equal(root.get("receiverStatus").as(String.class), condition.getReceiverStatus()));
                }
                if (condition.getEndTime() != null) {
                    list.add(cb.lessThanOrEqualTo(root.get("tMessage").get("sendedTime").as(Date.class), DateUtils.getEndOfDay(condition.getEndTime())));
                }
                if (StringUtils.isNotBlank(condition.getReceiverType())) {
                    list.add(cb.equal(root.get("receiverType").as(String.class), condition.getReceiverType()));
                }
                if (StringUtils.isNotBlank(condition.getKeyword())) {
                    list.add(cb.like(root.get("tMessage").get("sendTitle").as(String.class), "%" + condition.getKeyword() + "%"));
                }
            }
        });
        return messageReceiverDAO.findAll(messageReceiverSpecification, pageable);
    }

    /**
     * 根据多id修改消息状态为已读
     *
     * @param ids
     * @return
     */
    public void readMessage(List<String> ids) {
        if (ValidatorUtils.isArrayEmpty(ids)) {
            throw new ServiceException("更新出错，参数为空");
        }
        List<TMessageReceiver> receivers = messageReceiverDAO.findAll(ids);
        List<TMessageReceiver> updateResults = new LinkedList<TMessageReceiver>();
        for (TMessageReceiver messageReceiver : receivers) {
            if (BaseConstant.MessageReadStatus.MESSAGE_READ_STATUS_NON_READ.name().equals(messageReceiver.getReadStatus())) {
                messageReceiver.setReadStatus(BaseConstant.MessageReadStatus.MESSAGE_READ_STATUS_READ.name());
                messageReceiver.setReadTime(new Date());
                updateResults.add(messageReceiver);
            }
        }
        if(!updateResults.isEmpty()){
            messageReceiverDAO.save(updateResults);
        }
    }

    /**
     * 删除接收到的消息
     *
     * @param ids
     * @return
     */
    @Transactional
    public  List<TMessageReceiver> deleteMessageReceivers(List<String> ids) {
        if (ValidatorUtils.isArrayEmpty(ids)) {
            throw new ServiceException("删除出错，参数为空");
        }
        List<TMessageReceiver> tMessageReceivers = messageReceiverDAO.findAll(ids);
        messageReceiverDAO.delete(tMessageReceivers);
        return tMessageReceivers;
    }

    /**
     * 发送消息
     *
     * @param sysUserMember
     * @param title
     * @param content
     * @return
     */
    @Transactional
    public TMessage createMessage(SysUserMember sysUserMember, String title, String content) {
        TMessage tMessage = new TMessage();
        if (sysUserMember != null) {
            tMessage.setSenderName(sysUserMember.getUserName());
            tMessage.setMessageType(BaseConstant.MessageType.MESSAGE_TYPE_USER.name());
            tMessage.setFromUser(sysUserMember);
        } else {
            tMessage.setMessageType(BaseConstant.MessageType.MESSAGE_TYPE_SYSTEM.name());
        }
        tMessage.setSendContent(content);
        tMessage.setCreateTime(new Date());
        tMessage.setSendStatus(BaseConstant.MessageSendStatus.MESSAGE_SEND_STATUS_SENT.name());
        tMessage.setSendedTime(new Date());
        tMessage.setSendTitle(title);

        tMessageDAO.save(tMessage);
        return tMessage;
    }


    private TMessageReceiver createMessageReceiver(TMessage tMessage, String sendType, String target) {
        TMessageReceiver tMessageReceiver = new TMessageReceiver();
        tMessageReceiver.setReadStatus(BaseConstant.MessageReadStatus.MESSAGE_READ_STATUS_NON_READ.name());
        tMessageReceiver.setReceiver(target);
        tMessageReceiver.setReceiverStatus(BaseConstant.Result.RESULT_FAILURE.name());
        tMessageReceiver.setReceiverType(sendType);
        tMessageReceiver.settMessage(tMessage);
        messageReceiverDAO.save(tMessageReceiver);
        return tMessageReceiver;
    }

    /**
     * 发送邮件，通过邮箱发送
     *
     * @param title   标题
     * @param content 内容
     * @param emails  用户目标，通过用户的邮箱属性发送，没有邮箱或邮箱不正确将导致发送邮件失败
     * @return
     */
    @Transactional
    public TMessage sendToEmail(String title, String content, List<String> emails) {
        TMessage tMessage = createMessage(null, title, content);
        //用户用户邮箱
        List<TMessageReceiver> tMessageReceivers = new LinkedList<TMessageReceiver>();
        TMessage messageSource = new TMessage();
        messageSource.setId(tMessage.getId());
        for (String email : emails) {
            TMessageReceiver tMessageReceiver = createMessageReceiver(messageSource, BaseConstant.MessageSendType.MESSAGE_SEND_TYPE_EMAIL.name(),
                    email);
            tMessageReceivers.add(tMessageReceiver);
            _sendMessage(tMessage, tMessageReceiver);
        }
        //保存所有邮箱发送地址到接受者数据库
        tMessage.settMessageReceivers(tMessageReceivers);
        return tMessage;
    }

    private void _sendMessage(final TMessage tMessage, final TMessageReceiver tMessageReceiver) {
        for (MessageSendProxy messageSendProxy : messageSendProxies) {
            if (messageSendProxy.isValid(tMessageReceiver.getReceiverType())) {
                boolean success = messageSendProxy.send(tMessage, tMessageReceiver);
                if (success) {
                    tMessageReceiver.setReceiverStatus(BaseConstant.Result.RESULT_SUCCESS.name());
                    tMessageReceiver.setReadStatus(BaseConstant.MessageReadStatus.MESSAGE_READ_STATUS_NON_READ.name());
//                    tMessageReceiver.setReadTime(new Date());
                    messageReceiverDAO.save(tMessageReceiver);
                }
            }
        }
    }

    /**
     * 发送站内信，指定用户发送人
     *
     * @param from    发送来源
     * @param title   发送标题
     * @param content 发送内容
     * @param userIds 发送的目标成员
     * @return
     */
    @Transactional
    public TMessage sendToNotice(SysUserMember from, String title, String content, List<String> userIds) {
        TMessage tMessage = createMessage(from, title, content);
        TMessage messageSource = new TMessage();
        messageSource.setId(tMessage.getId());
        List<TMessageReceiver> tMessageReceivers = new LinkedList<TMessageReceiver>();
        for (String sysUserMember : userIds) {
            TMessageReceiver tMessageReceiver = createMessageReceiver(messageSource, BaseConstant.MessageSendType.MESSAGE_SEND_TYPE_NOTICE.name(),
                    sysUserMember);
            tMessageReceivers.add(tMessageReceiver);
            _sendMessage(tMessage, tMessageReceiver);
        }
        tMessage.settMessageReceivers(tMessageReceivers);
        return tMessage;
    }

    /**
     * 获取单条消息
     *
     * @param messageId
     * @return
     */
    public TMessage findOneMessage(String messageId) {
        return tMessageDAO.getOne(messageId);
    }

    /**
     * 批量删除消息
     *
     * @param ids
     * @return
     */
    @Transactional
    public List<TMessage> deleteMessages(List<String> ids) {
        if (ValidatorUtils.isArrayNotEmpty(ids)) {
            List<TMessage> messages = tMessageDAO.findAll(ids);
            tMessageDAO.delete(messages);
            return messages;
        }
        throw new ServiceException("参数出错");
    }

    /**
     * 查询在某个时间点以后是否有最新的消息
     * @param uid
     * @return
     */
    public long nonReadMessageTotal(String uid){
        final SysUserMember member = sysUserRepository.getOne(uid);
         Specification<TMessageReceiver> messageReceiverSpecification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TMessageReceiver>() {

             public void fillParam(Root<TMessageReceiver> root, CriteriaBuilder cb, List<Predicate> list) {
                 list.add(cb.equal(root.get("receiver").as(String.class), member.getId()));
                 list.add(cb.equal(root.get("readStatus").as(String.class), BaseConstant.MessageReadStatus.MESSAGE_READ_STATUS_NON_READ.name()));
             }
         });
        return messageReceiverDAO.count(messageReceiverSpecification);
    }

    /**
     * 查询在某个时间点以后是否有最新的消息
     * @param uid
     * @return
     */
    public Page<TMessageReceiver> nonReadMessages(String uid, Pageable pageable){
        final SysUserMember member = sysUserRepository.getOne(uid);
        Specification<TMessageReceiver> messageReceiverSpecification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TMessageReceiver>() {

            public void fillParam(Root<TMessageReceiver> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(cb.equal(root.get("receiver").as(String.class), member.getId()));
                list.add(cb.equal(root.get("readStatus").as(String.class), BaseConstant.MessageReadStatus.MESSAGE_READ_STATUS_NON_READ.name()));
            }
        });
        return messageReceiverDAO.findAll(messageReceiverSpecification, pageable);
    }

    /**
     * 查询消息主体
     *
     * @param messageCondition
     * @param pageable
     * @return
     */
    public Page<Message> findMessages(SearchMessageCondition messageCondition, Pageable pageable) {
        final SearchMessageCondition condition = messageCondition == null
                ? new SearchMessageCondition()
                : messageCondition;
        Specification<TMessage> specification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TMessage>() {

            public void fillParam(Root<TMessage> root, CriteriaBuilder cb, List<Predicate> list) {
                if (StringUtils.isNotBlank(condition.getSendTitle())) {
                    list.add(cb.like(root.get("sendTitle").as(String.class), "%" + condition.getSendTitle() + "%"));
                }
                if (StringUtils.isNotBlank(condition.getSendContent())) {
                    list.add(cb.like(root.get("sendContent").as(String.class), "%" + condition.getSendContent() + "%"));
                }
                if (condition.getFromUser() != null && StringUtils.isNotBlank(condition.getFromUser().getId())) {
                    list.add(cb.equal(root.get("fromUser").get("id").as(String.class), condition.getFromUser().getId()));
                }
                if (StringUtils.isNotBlank(condition.getMessageType())) {
                    list.add(cb.equal(root.get("messageType").as(String.class), condition.getMessageType()));
                }
                if (condition.getBeginSendTime() != null) {
                    list.add(cb.greaterThanOrEqualTo(root.get("sendedTime").as(Date.class), DateUtils.getStartOfDay(condition.getBeginSendTime())));
                }
                if (condition.getEndSendTime() != null) {
                    list.add(cb.lessThanOrEqualTo(root.get("sendedTime").as(Date.class), DateUtils.getEndOfDay(condition.getEndSendTime())));
                }
            }
        });
        Page<TMessage> messages = tMessageDAO.findAll(specification, pageable);
        List<Message> messageList = new LinkedList<Message>();
        for (TMessage tMessage : messages) {
            Message message = new Message();
            message.setMessage(tMessage);
            List<TMessageReceiver> receivers = tMessage.gettMessageReceivers();
            message.setSendTotal(receivers.size());
            int read = 0;
            for (TMessageReceiver receiver : receivers) {
                if (BaseConstant.MessageReadStatus.MESSAGE_READ_STATUS_READ.name().equals(receiver.getReadStatus())) {
                    read++;
                }
            }
            message.setId(tMessage.getId());
            message.setReadTotal(read);
            messageList.add(message);
        }
        return new PageImpl<Message>(messageList, pageable, messages.getTotalElements());
    }

    /**
     * 发送消息
     *
     * @param message
     * @param userIds
     */
    @Transactional
    public void sendMessage(TMessage message, List<String> userIds) {
        Set<String> targets = new HashSet<String>();
        if (ValidatorUtils.isArrayNotEmpty(userIds)) {
            targets.addAll(userIds);
        }else{
            targets = sysUserRepository.findAllId();
        }
        sendToNotice(AuthContextHelper.getLoginUser(), message.getSendTitle(), message.getSendContent(), new ArrayList<String>(targets));
    }


    /**
     * 任务统一发送消息接口
     *
     * @param title
     * @param content
     * @param userIds
     * @param args
     */
    @Transactional
    public String sendSystemMessage(String title, String content, List<String> userIds, final String... args) {
        try {
//            List<String> emails = QueryHelper.doQueryProperties(sysUserMembers, true, new QueryHelper.PropertyReader<SysUserMember, String>() {
//
//                public String read(SysUserMember sysUserMember) {
//                    return sysUserMember.getEmail();
//                }
//            });

            List<SysUserMember> members = sysUserRepository.findAll(userIds);
            List<String> names = QueryHelper.doQueryProperties(members, true, new QueryHelper.PropertyReader<SysUserMember, String>() {

                public String read(SysUserMember sysUserMember) {
                    return sysUserMember.getUserName();
                }
            });
            if (args != null) {
                final int len = args.length;
                if (len > 0) {
                    content = ConvertUtils.replace(content, "\\{\\d+\\}", new ReplaceOperate() {

                        public String replace(int i, String pattern) {
                            return i > len - 1 ? "" : args[i];
                        }
                    });
                }
            }
            LoggerFactory.getLogger(LogModules.TRACE_LOGGER).info("发送{}消息给{}等相关用户", title, StringUtils.join(names, ","));
            sendToNotice(null, title, content, userIds);
//            sendToEmail(title, content, emails);
            return content;
        } catch (Exception ex) {
            LoggerFactory.getLogger(LogModules.TRACE_LOGGER).error("发送消息失败");
        }
        return "";
    }
}
